import time,sys
import uuid
from copy import deepcopy
from jili.core.printlog import print
from xtquant.xttrader import XtQuantTrader, XtQuantTraderCallback
from xtquant.xttype import StockAccount
from xtquant import xtconstant
import datetime
from jili.ibot import notic_qywx
from jili.price import price_xtq as pricetool
from jili.pos.pos_frame_sim import pos_live

code2order_status={
xtconstant.ORDER_UNREPORTED:"submitting",
xtconstant.ORDER_WAIT_REPORTING:"submitting",
xtconstant.ORDER_REPORTED:"working",
xtconstant.ORDER_REPORTED_CANCEL:"working",
xtconstant.ORDER_PARTSUCC_CANCEL:"working",
xtconstant.ORDER_PART_CANCEL:"working",
xtconstant.ORDER_CANCELED:"canceled",
xtconstant.ORDER_PART_SUCC:"parttraded",
xtconstant.ORDER_SUCCEEDED:"alltraded",
xtconstant.ORDER_JUNK:"rejected",
xtconstant.ORDER_UNKNOWN:"submitting"
}
code2name_order_status={
xtconstant.ORDER_UNREPORTED:"未报",
xtconstant.ORDER_WAIT_REPORTING:"待报",
xtconstant.ORDER_REPORTED:"已报",
xtconstant.ORDER_REPORTED_CANCEL:"已报待撤",
xtconstant.ORDER_PARTSUCC_CANCEL:"部成待撤",
xtconstant.ORDER_PART_CANCEL:"部撤",
xtconstant.ORDER_CANCELED:"已撤",
xtconstant.ORDER_PART_SUCC:"部成",
xtconstant.ORDER_SUCCEEDED:"已成",
xtconstant.ORDER_JUNK:"废单",
xtconstant.ORDER_UNKNOWN:"未知"
}
code2name_order_type={
xtconstant.STOCK_BUY:"买入",
xtconstant.STOCK_SELL:"卖出",
xtconstant.CREDIT_FIN_BUY:"融资买入",
xtconstant.CREDIT_SLO_SELL:"融券卖出",
xtconstant.CREDIT_BUY_SECU_REPAY:"买券还券",
xtconstant.CREDIT_DIRECT_SECU_REPAY:"直接还券",
xtconstant.CREDIT_SELL_SECU_REPAY:"卖券还款",
xtconstant.CREDIT_DIRECT_CASH_REPAY:"直接还款"

}
code2name_price_type={
xtconstant.LATEST_PRICE:"最新价",
xtconstant.FIX_PRICE:"指定价",
xtconstant.MARKET_SH_CONVERT_5_CANCEL:"上交所最优五档即时成交剩余撤销",
xtconstant.MARKET_SH_CONVERT_5_LIMIT:"上交所最优五档即时成交剩转限价",
xtconstant.MARKET_PEER_PRICE_FIRST:"上交所对手方最优价格委托",
xtconstant.MARKET_MINE_PRICE_FIRST:"上交所本方最优价格委托",
xtconstant.MARKET_PEER_PRICE_FIRST:"深交所对手方最优价格委托",
xtconstant.MARKET_MINE_PRICE_FIRST:"深交所本方最优价格委托",
xtconstant.MARKET_SZ_INSTBUSI_RESTCANCEL:"深交所即时成交剩余撤销委托",
xtconstant.MARKET_SZ_CONVERT_5_CANCEL:"深交所最优五档即时成交剩余撤销",
xtconstant.MARKET_SZ_FULL_OR_CANCEL:"深交所全额成交或撤销委托"
}
def xt2dict(c):
    cc={}
    for i in dir(c):
        if i.startswith("_"):
            continue
        cc[i]=getattr(c,i)
    return cc


def ats2xt_price_type(pricetype):
    if pricetype == "market":
        return xtconstant.LATEST_PRICE
    else:
        return xtconstant.FIX_PRICE
def xt2ats_price_type(pricetype):
    if pricetype == xtconstant.FIX_PRICE:
        return "limit"
    else:
        return "market"

def ats2xt_order_type(account_type,openclose, bs):
    """
    融资买入 - xtconstant.CREDIT_FIN_BUY
    融券卖出 - xtconstant.CREDIT_SLO_SELL
    买券还券 - xtconstant.CREDIT_BUY_SECU_REPAY
    直接还券 - xtconstant.CREDIT_DIRECT_SECU_REPAY
    卖券还款 - xtconstant.CREDIT_SELL_SECU_REPAY
    直接还款 - xtconstant.CREDIT_DIRECT_CASH_REPAY
    """
    order_type = None
    if account_type == xtconstant.SECURITY_ACCOUNT:#股票
        if openclose == "primary":
            if bs == "buy":
                order_type = xtconstant.ETF_PURCHASE#ETF申购
            elif bs == "sell":
                order_type = xtconstant.ETF_REDEMPTION#ETF赎回
        else:
            if bs == "buy":
                order_type = xtconstant.STOCK_BUY#买入
            elif bs == "sell":
                order_type = xtconstant.STOCK_SELL#卖出
    elif account_type == xtconstant.CREDIT_ACCOUNT:#信用
        if openclose == "open":
            if bs == "buy":
                order_type = xtconstant.STOCK_BUY#担保品买入
            elif bs == "sell":
                order_type = xtconstant.CREDIT_SLO_SELL#融券卖出
        elif openclose == "close":
            if bs == "sell":
                order_type = xtconstant.STOCK_SELL#担保品卖出
            elif bs == "buy":
                order_type = xtconstant.CREDIT_BUY_SECU_REPAY#买券还券
        elif openclose =="credit":
            if bs == "buy":
                order_type = xtconstant.CREDIT_FIN_BUY#融资买入
            elif bs == "sell":
                order_type = xtconstant.CREDIT_SELL_SECU_REPAY#卖券还款
        elif openclose == "debt":
            if bs == "buy":
                order_type = xtconstant.CREDIT_DIRECT_CASH_REPAY#直接还款
            elif bs == "sell":
                order_type = xtconstant.CREDIT_DIRECT_SECU_REPAY#直接还券
    elif account_type == xtconstant.FUTURE_ACCOUNT or account_type == xtconstant.FUTURE_OPTION_ACCOUNT:
        if openclose=="open":
            if bs == "buy":
                order_type = xtconstant.FUTURE_OPEN_LONG#开多
            elif bs == "sell":
                order_type = xtconstant.FUTURE_OPEN_SHORT#开空
        elif openclose=="close":
            if bs == "buy":
                order_type = xtconstant.FUTURE_CLOSE_LONG_HISTORY_FIRST#平多,历史优先
            elif bs == "sell":
                order_type = xtconstant.FUTURE_CLOSE_SHORT_HISTORY_FIRST#平空,历史优先
        elif openclose=="close_yesterday":
            if bs == "buy":
                order_type = xtconstant.FUTURE_CLOSE_LONG_HISTORY#平多昨
            elif bs == "sell":
                order_type = xtconstant.FUTURE_CLOSE_SHORT_HISTORY#平空昨
        elif openclose=="close_today":
            if bs == "buy":
                order_type = xtconstant.FUTURE_CLOSE_LONG_TODAY#平多今
            elif bs == "sell":
                order_type = xtconstant.FUTURE_CLOSE_SHORT_TODAY#平空今
        elif openclose == "deal_option":
            order_type = xtconstant.OPTION_FUTURE_OPTION_EXERCISE#期货期权行权
    elif account_type == xtconstant.STOCK_OPTION_ACCOUNT:#股票期权
        if openclose=="open":
            if bs == "buy":
                order_type = xtconstant.STOCK_OPTION_BUY_OPEN#买入开仓
            elif bs == "sell":
                order_type = xtconstant.STOCK_OPTION_SELL_OPEN#卖出开仓
        elif openclose=="close":
            if bs == "buy":
                order_type = xtconstant.STOCK_OPTION_BUY_CLOSE#买入平仓
            elif bs == "sell":
                order_type = xtconstant.STOCK_OPTION_SELL_CLOSE#卖出平仓
        elif openclose=="hold":
            if bs == "buy":
                order_type = xtconstant.STOCK_OPTION_COVERED_OPEN#备兑开仓
            elif bs == "sell":
                order_type = xtconstant.STOCK_OPTION_COVERED_CLOSE#备兑平仓
        elif openclose == "deal_option":
            if bs == "buy":
                order_type = xtconstant.STOCK_OPTION_CALL_EXERCISE#认购行权
            elif bs == "sell":
                order_type = xtconstant.STOCK_OPTION_PUT_EXERCISE#认沽行权
            elif openclose == "lock":
                order_type = xtconstant.STOCK_OPTION_SECU_LOCK#证券锁定
            elif openclose == "unlock":
                order_type = xtconstant.STOCK_OPTION_SECU_UNLOCK#证券解锁
    if order_type is None:
        info = "不认识的指令" + str(account_type)+"," +openclose+","+bs
        print("sys", info)
        return None
    return order_type

xt2ats_bs={
xtconstant.DIRECTION_FLAG_LONG:"buy",
xtconstant.DIRECTION_FLAG_SHORT:"sell",
}
xt2ats_openclose={
xtconstant.OFFSET_FLAG_OPEN:"open",
xtconstant.OFFSET_FLAG_CLOSE:"close",
xtconstant.OFFSET_FLAG_FORCECLOSE:"close_force",
xtconstant.OFFSET_FLAG_CLOSETODAY:"close_today",
xtconstant.OFFSET_FLAG_ClOSEYESTERDAY:"close_yesterday",
}
def xt2openclose(account_type,order_type, price_type,direction,offset_flag):
    info = ""
    openclose=None
    bs=None
    if account_type==xtconstant.SECURITY_ACCOUNT:
        if order_type == xtconstant.STOCK_BUY:
            openclose, bs = "open", "buy"
        elif order_type == xtconstant.STOCK_SELL:
            openclose, bs = "close", "sell"
        elif order_type == xtconstant.ETF_PURCHASE:
            openclose, bs = "primary", "buy"
        elif order_type == xtconstant.ETF_REDEMPTION:
            openclose, bs = "primary", "sell"
    elif account_type==xtconstant.CREDIT_ACCOUNT:
        if order_type in [xtconstant.STOCK_BUY, xtconstant.CREDIT_BUY]:
            openclose, bs = "open", "buy"
        elif order_type in [xtconstant.STOCK_SELL, xtconstant.CREDIT_SELL]:
            openclose, bs = "close", "sell"
        elif order_type == xtconstant.CREDIT_FIN_BUY:
            openclose, bs = "credit", "buy"
        elif order_type == xtconstant.CREDIT_SLO_SELL:
            openclose, bs = "open", "sell"
        elif order_type == xtconstant.CREDIT_BUY_SECU_REPAY:
            openclose, bs = "close", "buy"
        elif order_type == xtconstant.CREDIT_SELL_SECU_REPAY:
            openclose, bs = "credit", "sell"
        elif order_type == xtconstant.CREDIT_DIRECT_CASH_REPAY:
            openclose, bs = "debt", "buy"
        elif order_type == xtconstant.CREDIT_DIRECT_SECU_REPAY:
            openclose, bs = "debt", "sell"
    elif account_type==xtconstant.FUTURE_ACCOUNT:
        bs=xt2ats_bs.get(direction)
        openclose=xt2ats_openclose.get(offset_flag,"close")
    elif account_type==xtconstant.STOCK_OPTION_ACCOUNT or account_type==xtconstant.FUTURE_OPTION_ACCOUNT:
        bs = xt2ats_bs.get(direction)
        openclose = xt2ats_openclose.get(offset_flag, "close")
    if openclose is None:
        info = "不认识的指令" + str(account_type)+str(order_type)+str(price_type)+str(direction)+str(offset_flag)
        print("sys", "不认识的指令", account_type,order_type, price_type,direction,offset_flag)
    info = info + code2name_order_type[order_type] + "," + str(account_type)+str(order_type)+str(price_type)+str(direction)+str(offset_flag)  # xtdict.code2name_oprice_type[price_type]
    """
    股票

    买入 - xtconstant.STOCK_BUY
    卖出 - xtconstant.STOCK_SELL
    信用

    担保品买入 - xtconstant.CREDIT_BUY
    担保品卖出 - xtconstant.CREDIT_SELL
    融资买入 - xtconstant.CREDIT_FIN_BUY
    融券卖出 - xtconstant.CREDIT_SLO_SELL
    买券还券 - xtconstant.CREDIT_BUY_SECU_REPAY
    直接还券 - xtconstant.CREDIT_DIRECT_SECU_REPAY
    卖券还款 - xtconstant.CREDIT_SELL_SECU_REPAY
    直接还款 - xtconstant.CREDIT_DIRECT_CASH_REPAY
    专项融资买入 - xtconstant.CREDIT_FIN_BUY_SPECIAL
    专项融券卖出 - xtconstant.CREDIT_SLO_SELL_SPECIAL
    专项买券还券 - xtconstant.CREDIT_BUY_SECU_REPAY_SPECIAL
    专项直接还券 - xtconstant.CREDIT_DIRECT_SECU_REPAY_SPECIAL
    专项卖券还款 - xtconstant.CREDIT_SELL_SECU_REPAY_SPECIAL
    专项直接还款 - xtconstant.CREDIT_DIRECT_CASH_REPAY_SPECIAL
    期货六键风格

    开多 - xtconstant.FUTURE_OPEN_LONG
    平昨多 - xtconstant.FUTURE_CLOSE_LONG_HISTORY
    平今多 - xtconstant.FUTURE_CLOSE_LONG_TODAY
    开空 - xtconstant.FUTURE_OPEN_SHORT
    平昨空 - xtconstant.FUTURE_CLOSE_SHORT_HISTORY
    平今空 - xtconstant.FUTURE_CLOSE_SHORT_TODAY
    期货四键风格

    平多，优先平今 - xtconstant.FUTURE_CLOSE_LONG_TODAY_FIRST
    平多，优先平昨 - xtconstant.FUTURE_CLOSE_LONG_HISTORY_FIRST
    平空，优先平今 - xtconstant.FUTURE_CLOSE_SHORT_TODAY_FIRST
    平空，优先平昨 - xtconstant.FUTURE_CLOSE_SHORT_HISTORY_FIRST
    期货两键风格

    卖出，如有多仓，优先平仓，优先平今，如有余量，再开空 - xtconstant.FUTURE_CLOSE_LONG_TODAY_HISTORY_THEN_OPEN_SHORT
    卖出，如有多仓，优先平仓，优先平昨，如有余量，再开空 - xtconstant.FUTURE_CLOSE_LONG_HISTORY_TODAY_THEN_OPEN_SHORT
    买入，如有空仓，优先平仓，优先平今，如有余量，再开多 - xtconstant.FUTURE_CLOSE_SHORT_TODAY_HISTORY_THEN_OPEN_LONG
    买入，如有空仓，优先平仓，优先平昨，如有余量，再开多 - xtconstant.FUTURE_CLOSE_SHORT_HISTORY_TODAY_THEN_OPEN_LONG
    买入，不优先平仓 - xtconstant.FUTURE_OPEN
    卖出，不优先平仓 - xtconstant.FUTURE_CLOSE
    期货 - 跨商品套利

    开仓 - xtconstant.FUTURE_ARBITRAGE_OPEN
    平, 优先平昨 - xtconstant.FUTURE_ARBITRAGE_CLOSE_HISTORY_FIRST
    平, 优先平今 - xtconstant.FUTURE_ARBITRAGE_CLOSE_TODAY_FIRST
    期货展期

    看多, 优先平昨 - xtconstant.FUTURE_RENEW_LONG_CLOSE_HISTORY_FIRST
    看多，优先平今 - xtconstant.FUTURE_RENEW_LONG_CLOSE_TODAY_FIRST
    看空，优先平昨 - xtconstant.FUTURE_RENEW_SHORT_CLOSE_HISTORY_FIRST
    看空，优先平今 - xtconstant.FUTURE_RENEW_SHORT_CLOSE_TODAY_FIRST
    股票期权

    买入开仓，以下用于个股期权交易业务 - xtconstant.STOCK_OPTION_BUY_OPEN
    卖出平仓 - xtconstant.STOCK_OPTION_SELL_CLOSE
    卖出开仓 - xtconstant.STOCK_OPTION_SELL_OPEN
    买入平仓 - xtconstant.STOCK_OPTION_BUY_CLOSE
    备兑开仓 - xtconstant.STOCK_OPTION_COVERED_OPEN
    备兑平仓 - xtconstant.STOCK_OPTION_COVERED_CLOSE
    认购行权 - xtconstant.STOCK_OPTION_CALL_EXERCISE
    认沽行权 - xtconstant.STOCK_OPTION_PUT_EXERCISE
    证券锁定 - xtconstant.STOCK_OPTION_SECU_LOCK
    证券解锁 - xtconstant.STOCK_OPTION_SECU_UNLOCK
    期货期权

    期货期权行权 - xtconstant.OPTION_FUTURE_OPTION_EXERCISE
    ETF申赎

    申购 - xtconstant.ETF_PURCHASE
    赎回 - xtconstant.ETF_REDEMPTION
    """
    return openclose, bs, info
xt2ats_posbs={
xtconstant.DIRECTION_FLAG_LONG:"buy",
xtconstant.DIRECTION_FLAG_SHORT:"sell",
}

class onTraderTool(XtQuantTraderCallback):
    def __init__(self,tradertool,account,ss,ssconfig):
        self.tradertool=tradertool
        self.orders_watchdog={}
        self.account=account
        self.orders_watchdog_closeall = {}
        self.ss=ss
        self.working_orders={}
        self.isibot=False
        self.qywx_bot_key=""
        self.open_objs=[]
        self.reset_order_checktick={}
        self.pricetool0=pricetool.pricetool()
        if "strategy_name" in ssconfig.keys():
            self.ssname = ssconfig["strategy_name"]
        else:
            self.ssname = ""
        self.strategy_name0=ssconfig.get("strategy_name0","")
        if "qywx_bot_key" in ssconfig.keys():
            self.isibot=True
            self.qywx_bot_key=ssconfig["qywx_bot_key"]
        self.order_end_stat=[xtconstant.ORDER_PART_CANCEL,xtconstant.ORDER_CANCELED,xtconstant.ORDER_PART_SUCC,xtconstant.ORDER_SUCCEEDED,xtconstant.ORDER_JUNK]
    def settradetool(self,tradertool):
        self.tradertool = tradertool
    def deal_reset_order(self,obj, orderid, o):
        if "resetprice" in o.keys():
            price=o["resetprice"]
            if obj not in self.reset_order_checktick.keys():
                self.reset_order_checktick[obj] = {}
            self.reset_order_checktick[obj][orderid]=price
    def deal_close_all(self,obj,signal,t,pos):
        isdeal=True
        if obj in self.working_orders.keys():
            t=self.working_orders[obj]
            if t:
                # 撤单，撤单成功后委托
                for orderid,o in t.items():
                    self.tradertool.cancel_order_stock(self.account, o.order_id)
                self.orders_watchdog_closeall[obj] = [signal,t,pos]
                isdeal=False
        if isdeal:
            if pos.can_use_volume>0:
                orderid = self.tradertool.order_stock(self.account, obj, xtconstant.STOCK_SELL, pos.can_use_volume,
                                                  xtconstant.FIX_PRICE,
                                                  t["lastPrice"], self.ssname, signal["k"]+signal["msg"])
                now = datetime.datetime.now()
                self.set_watchdog_order(obj, now, orderid)
                print("deal_close_all", self.orders_watchdog)
            else:
                print("deal_close_all", obj, "持仓为0")
    def set_watchdog_order(self,obj,timekey,orderid):
        if obj not in self.orders_watchdog.keys():
            self.orders_watchdog[obj]={}
        self.orders_watchdog[obj][orderid]=[timekey,None,None]
    def ontick(self,obj,timekey,t):
        if obj in self.reset_order_checktick.keys():
            oo=self.reset_order_checktick[obj]
            if oo:
                p=t["lastPrice"]
                deals=[]
                for orderid,price in oo.items():
                    if p<=price:
                        now = datetime.datetime.now()-datetime.timedelta(hours=1)
                        self.set_watchdog_order(obj, now, orderid)
                        deals.append(orderid)
                if deals:
                    for i in deals:
                        del oo[i]
            if not oo:
                del self.reset_order_checktick[obj]
        if obj in self.orders_watchdog.keys():
            o=self.orders_watchdog[obj]
            for orderid,oo in o.items():
                oo[2]=t
                delt=timekey-oo[0]
                delt=delt.total_seconds()
                if delt>=15:
                    self.tradertool.cancel_order_stock(self.account, orderid)
    def deal_ibot_order(self,r):
        # print("ibot",self.qywx_bot_key,self.isibot,r.stock_code,r.traded_volume,r.strategy_name,r.order_type,r.order_status)
        try:
            if self.isibot:
                obj=r.stock_code
                name=obj
                info=self.pricetool0.get_objinfo(obj)
                if info:
                    if "InstrumentName" in info.keys():
                        name=info["InstrumentName"]
                if r.strategy_name == self.ssname:
                    s=""
                    if r.order_status == xtconstant.ORDER_SUCCEEDED:  # xtconstant.ORDER_PART_CANCEL
                        s = self.strategy_name0+":"+name + ':全部成交 '
                    elif r.order_status == xtconstant.ORDER_PART_CANCEL:
                        s = self.strategy_name0+":"+name + ':部成部撤 '
                    elif r.order_status == xtconstant.ORDER_REPORTED:
                        s = self.strategy_name0+":"+name + ':已报单 '
                    elif r.order_status == xtconstant.ORDER_CANCELED:
                        s = self.strategy_name0+":"+name + ':已撤单 '
                    elif r.order_status == xtconstant.ORDER_JUNK:
                        s = self.strategy_name0+":"+name + ':废单 '
                    else:
                        print("不是最终状态")
                    if s!="":
                        n0=code2name_order_type.get(r.order_type,r.order_type)
                        if "买" in n0:
                            s=s+'\n>**<font color="warning">'
                        elif "卖" in n0:
                            s=s+'\n>**<font color="info">'
                        else:
                            s=s+'\n>**<font color="comment">'
                        s = s+n0+'</font>**  \n>股票代码：' + obj +' \n>报单价格：' + str(
                            r.price) +' \n>报单数量：' + str(r.order_volume) +' \n>订单说明：' + str(r.order_remark)
                        print("ibot msg:", s)
                        notic_qywx.send_markdown(s, self.qywx_bot_key)
                    else:
                        print("不是最终状态")
                else:
                    print("ibot,不是本策略")
            else:
                print("ibot,没有配置机器人")
        except Exception as e:
            print("deal_ibot_order err",e)
    def on_stock_order(self, r):
        """
        委托回报推送
        :param order: XtOrder对象
        :return:
        """

        print("on_stock_order",r.stock_code,r.traded_volume,r.traded_volume)
        try:
            self.ss.on_order(r)
            #self.ss.deal_out_objs(r)
            obj=r.stock_code
            orderid=r.order_id
            stat = r.order_status
            if r.order_volume>r.traded_volume:
                if obj in self.orders_watchdog.keys():
                    if orderid in self.orders_watchdog[obj].keys():
                        self.orders_watchdog[obj][orderid][1]=r
            elif r.order_volume>r.traded_volume:
                if obj in self.orders_watchdog.keys():
                    if orderid in self.orders_watchdog[obj].keys():
                        del self.orders_watchdog[obj][orderid]
                if len(self.orders_watchdog[obj])==0:
                    del self.orders_watchdog[obj]
            if obj not in self.working_orders.keys():
                self.working_orders[obj]={}
            self.working_orders[obj][orderid]=r
            if stat in self.order_end_stat:
                del self.working_orders[obj][orderid]
            if len(self.working_orders[obj])==0:
                if obj in self.orders_watchdog_closeall.keys():
                    signal, t, pos=self.orders_watchdog_closeall[obj]
                    orderid = self.tradertool.order_stock(self.account, obj, xtconstant.STOCK_SELL, pos.can_use_volume,
                                                          xtconstant.FIX_PRICE,
                                                          t["lastPrice"], self.ssname, signal["msg"])
                    now = datetime.datetime.now()
                    self.set_watchdog_order(obj, now, orderid)
                    print("on_stock_order", self.orders_watchdog)
            if r.strategy_name==self.ssname:
                print('本策略-委托回调',"strategy_name",r.strategy_name,r.order_remark,obj,orderid,r.order_type,r.order_status,code2name_order_status[r.order_status],r.status_msg,"order_volume",r.order_volume,r.price,"traded_volume",r.traded_volume,r.price_type)
            else:
                print('外部-委托回调',"strategy_name",r.strategy_name,r.order_remark,obj,orderid,r.order_type,r.order_status,code2name_order_status[r.order_status],"order_volume",r.order_volume,"traded_volume",r.price,r.traded_volume,r.traded_volume,r.price_type)
            self.deal_ibot_order(r)
            # if r.traded_volume > 0 and r.order_status in [xtconstant.ORDER_SUCCEEDED,xtconstant.ORDER_PART_CANCEL]:
            #     if r.strategy_name==self.ssname or r.stock_code in self.ss.out_objs:
            #         self.ss.refresh_pos()
        except Exception as e:
            print("on_stock_order err",e)




    def on_stock_trade(self, r):
        """
        成交变动推送
        :param trade: XtTrade对象
        :return:
        """
        if r.strategy_name==self.ssname:
            print('本策略-成交',"strategy_name",r.strategy_name,r.order_remark,r.stock_code,r.order_id,"order_type",r.order_type,code2name_order_type[r.order_type],datetime.datetime.fromtimestamp(r.traded_time),"traded_price",r.traded_price,"traded_volume",r.traded_volume,"traded_amount",r.traded_amount)

        else:
            print('外部-成交',"strategy_name",r.strategy_name,r.order_remark,r.stock_code,r.order_id,"order_type",r.order_type,code2name_order_type[r.order_type],datetime.datetime.fromtimestamp(r.traded_time),"traded_price",r.traded_price,"traded_volume",r.traded_volume)


    def on_order_error(self, r):
        """
        委托失败推送
        :param order_error:XtOrderError 对象
        :return:
        """
        # print("on order_error callback")
        # print(order_error.order_id, order_error.error_id, order_error.error_msg)
        if r.strategy_name == self.ssname:
            print('本策略-下单错误', "strategy_name", r.strategy_name, r.order_id, r.error_id,r.error_msg)
            s = self.strategy_name0+':下单失败 \n>委托ID：' +str(r.order_id) + '  \n>错误ID：' + str(r.error_id) + '  \n>说明：' + str(r.error_msg)
            print("ibot msg:", s)
            notic_qywx.send_markdown(s, self.qywx_bot_key)

    def on_cancel_error(self, r):
        """
        撤单失败推送
        :param cancel_error: XtCancelError 对象
        :return:
        """
        if r.strategy_name == self.ssname:
            print('本策略-撤单错误', "strategy_name", r.strategy_name, r.order_id, r.error_id, r.error_msg)
            s = self.strategy_name0+':撤单失败 \n>委托ID：' +str(r.order_id) + '  \n>错误ID：' + str(r.error_id) + '  \n>说明：' + str(r.error_msg)
            print("ibot msg:", s)
            notic_qywx.send_markdown(s, self.qywx_bot_key)

    def on_order_stock_async_response(self, r):
        """
        异步下单回报推送
        :param r: XtOrderResponse 对象
        :return:
        """
        if r.strategy_name == self.ssname:
            print(f"异步委托回调 {r.order_remark}")

    def on_cancel_order_stock_async_response(self, r):
        """
        :param r: XtCancelOrderResponse 对象
        :return:
        """
        if r.strategy_name == self.ssname:
            print("异步撤单回调", sys._getframe().f_code.co_name)


    def on_account_status(self, r):
        """
        :param r: XtAccountStatus 对象
        :return:
        """
        print("账号状态",r.account_id,r.broker_type,r.platform_id,r.account_classification,r.login_status)
class tradetool():
    def __init__(self,config):
        self.fund={}#资金
        self.pos={}#持仓
        self.orders=[]#今日委托
        self.working_orders={}#当前挂单
        self.working_orders_byobj={}
        self.trades=[]
        self.connect_result=-1
        self.sim_pos=None
        self.pos_run_mode=config.get("pos_run_mode","live")
        if self.pos_run_mode=="live":
            if "qmt_path" in config.keys():
                session_id = int(time.time())
                self.tradertool = XtQuantTrader(config["qmt_path"], session_id)
                # 创建资金账号为 800068 的证券账号对象
                self.account = StockAccount(config["account_id"], config["account_type"])
                self.investorid=config["account_id"]
                self.account_type=config["account_type"].lower()#stock,credit,future
                # 创建交易回调类对象，并声明接收回调
                self.ontradertool = onTraderTool(self.tradertool,self.account,self,config)
                self.tradertool.register_callback(self.ontradertool)
                # 启动交易线程
                self.tradertool.start()
                # 建立交易连接，返回0表示连接成功
                connect_result = self.tradertool.connect()
                if connect_result!=0:
                    print("交易连接失败，错误代码为",connect_result)
                    print("请检查后再启动策略")
                    # sys.exit()
                print('建立交易连接，连接成功', connect_result)
                subscribe_result = self.tradertool.subscribe(self.account)
                print('对交易回调进行订阅，订阅后可以收到交易主推，返回0表示订阅成功', subscribe_result)
                self.connect_result=connect_result
        else:
            self.sim_pos=config["pos"]
            self.orders=self.sim_pos.orders_book
            self.working_orders = self.sim_pos.orders_working_book  # 当前挂单
            self.working_orders_byobj =self.sim_pos.orders_working_book_byobj
        # return self.connect_result
    def login(self,qmt_path=None,account_id=None,account_type=None):
        if self.pos_run_mode=="live":
            if qmt_path is not None and account_id is not None and account_type is not None:
                session_id = int(time.time())
                self.tradertool = XtQuantTrader(qmt_path, session_id)
                # 创建资金账号为 800068 的证券账号对象
                self.investorid = account_id
                self.account = StockAccount(account_id, account_type)
                self.account_type = account_type.lower()
                # 创建交易回调类对象，并声明接收回调
                self.ontradertool = onTraderTool(self.tradertool, self.account, self, self.config)
                self.tradertool.register_callback(self.ontradertool)
                # 启动交易线程
                self.tradertool.start()
                # 建立交易连接，返回0表示连接成功
                connect_result = self.tradertool.connect()
                if connect_result != 0:
                    print("交易连接失败，错误代码为", connect_result)
                    print("请检查后再启动策略")
                    # sys.exit()
                print('建立交易连接，连接成功', connect_result)
                subscribe_result = self.tradertool.subscribe(self.account)
                print('对交易回调进行订阅，订阅后可以收到交易主推，返回0表示订阅成功', subscribe_result)
                self.connect_result = connect_result
            self.qry_info()
            return self.connect_result
        else:
            self.qry_info()
            return 0
            # brokerid="sim"
            # investorid=self.config["account_id"]
            # password=""
            # return super().login(brokerid,investorid,password,channel="ctp.0",info={},issync_tradedata=True)
    def get_version(self):
        return "20250526"
    def logout(self):
        if self.pos_run_mode == "live":
            self.tradertool.stop()
        return True
    def cancel_order(self,orderid):
        if self.pos_run_mode == "live":
            self.tradertool.cancel_order_stock(self.account, orderid)
        else:
            # r=super().cancel_order(orderid)
            if orderid in self.working_orders.keys():
                o=self.working_orders[orderid]
                oo=self.working_orders_byobj[o["obj"]]
                if len(oo)==1:
                    del self.working_orders_byobj[o["obj"]]
                else:
                    del self.working_orders_byobj[o["obj"]][orderid]
                o["qty0"]=0
                o["statusmsg"]="撤单"
                o["status"]="canceled"
                del self.working_orders[orderid]
            return True
    def qry_info(self,req={}):
        if self.pos_run_mode == "live":
            self.get_fund()
            self.get_pos()
            self.get_orders()
            self.get_trade()
        else:
            self.get_fund()
            self.get_pos()
            self.get_orders()
            # super().qry_info()
    def deal_order(self,c,orderid=None):
        obj, mkcode = pricetool.code2obj(c.stock_code)
        if orderid is None:
            orderid=c.order_id
        #direction,offset_flag
        pricetype = xt2ats_price_type(c.order_type)
        status = code2order_status[c.order_status]
        statusmsg = code2name_order_status[c.order_status]
        openclose, bs, orderinfo = xt2openclose(c.account_type,c.order_type, c.price_type,c.direction,c.offset_flag)
        order = {"obj": obj, "code": c.stock_code, "mkcode": mkcode, "bs": bs, "openclose": openclose,
                 "orderid": orderid, "sysid": c.order_sysid, "qty": c.order_volume, "qty1": c.traded_volume,
                 "price": c.price, "price1": c.traded_price, "status": status,
                 "statusmsg": statusmsg + ":" + str(c.order_status) + c.status_msg,
                 "ssid": c.strategy_name, "orderinfo": c.order_remark + "," + orderinfo}
        order["qty0"] = c.order_volume - c.traded_volume
        order["pricetype"]=pricetype
        order["timekey"] = datetime.datetime.fromtimestamp(c.order_time)
        for k, v in xt2dict(c).items():
            if k not in order.keys():
                order[k] = v
        return order
    def get_orders(self,isworking=False):
        if self.pos_run_mode != "live":
            if isworking:
                return self.working_orders
            else:
                return self.orders
        else:
            """
            stock_code	str	证券代码，例如"600000.SH"
            order_id	int	订单编号
            order_sysid	str	柜台合同编号
            order_time	int	报单时间
            order_type	int	委托类型，参见数据字典
            order_volume	int	委托数量
            price_type	int	报价类型，该字段在返回时为柜台返回类型，不等价于下单传入的price_type，枚举值不一样功能一样，参见数据字典
            price	float	委托价格
            traded_volume	int	成交数量
            traded_price	float	成交均价
            order_status	int	委托状态，参见数据字典
            status_msg	str	委托状态描述，如废单原因
            strategy_name	str	策略名称
            order_remark	str	委托备注，最大 24 个英文字符
            :param isworking:
            :return:
            """
            self.working_orders = {}
            oo=[]
            os=self.tradertool.query_stock_orders(self.account, cancelable_only = isworking)

            if os:
                for c in os:
                    order=self.deal_order(c)
                    oo.append(order)
                    if order["qty0"]>0:
                        self.working_orders[order["orderid"]]=order
                        print("working_orders", order)
                    else:
                        print("orders", order)
            self.orders=oo
            return oo
    def get_pos(self):
        if self.pos_run_mode != "live":
            self.sim_pos.getfundpos()
            self.pos=deepcopy(self.sim_pos.pos)
            for obj,v in self.pos.items():
                for bs,p in v.items():
                    if bs=="buy":
                        p["can_use_volume"]=p["qty"]
                        if obj in self.working_orders_byobj.keys():
                            for orderid,order in self.working_orders_byobj[obj].items():
                                if order["qty0"]>0 and order["bs"]=="sell":
                                    p["can_use_volume"]=p["can_use_volume"]-order["qty0"]
                        for order in self.orders:
                            if order["obj"]==obj and order["bs"]=="buy":
                                p["can_use_volume"] = p["can_use_volume"] - order["qty1"]
                    p["price"] = p["costprice"]
                    p["qty_avl"]=p["can_use_volume"]
                    p["code"]=pricetool.obj2code(obj)
                    p["bs"]=bs
            return self.pos
        else:
            """
            stock_code	str	证券代码
            volume	int	持仓数量
            can_use_volume	int	可用数量
            open_price	float	开仓价
            market_value	float	市值
            frozen_volume	int	冻结数量
            on_road_volume	int	在途股份
            yesterday_volume	int	昨夜拥股
            avg_price	float	成本价
            direction	int	多空方向，股票不适用；参见数据字典
    
            :return:
            """

            position = self.tradertool.query_stock_positions(self.account)
            poss={}
            if position:
                for c in position:
                    if c.volume!=0:
                        obj,mkcode=pricetool.code2obj(c.stock_code)
                        bs=xt2ats_posbs[c.direction]
                        pos = {"obj": obj, "code": c.stock_code,"mkcode":mkcode,"bs":bs,
                                 "qty": c.volume, "qty_avl": c.can_use_volume,"qty_yesterday":c.yesterday_volume,"qty_today":c.volume-c.can_use_volume,"qty_credit":0,
                                 "price": c.avg_price, "margin": c.market_value}
                        for k, v in xt2dict(c).items():
                            if k not in pos.keys():
                                pos[k] = v
                        if obj not in poss.keys():
                            poss[obj]={}
                        poss[obj][bs]=pos
                        print("pos", pos)
            if self.account_type == "credit":#CREDIT
                datas = self.tradertool.query_stk_compacts(self.account)
                if datas:
                    for c in datas:
                        if c.real_compact_vol != 0:
                            if c.compact_type==48:
                                obj,mkcode=pricetool.code2obj(c.instrument_id)
                                if obj in poss.keys():
                                    if "buy" in poss[obj].keys():
                                        poss[obj]["buy"]["qty_credit"]=c.real_compact_vol
                            elif c.compact_type==49:
                                obj, mkcode = pricetool.code2obj(c.instrument_id)
                                if obj in poss.keys():
                                    if "buy" in poss[obj].keys():
                                        poss[obj]["buy"]["qty_credit"] = -c.real_compact_vol
            self.pos=poss
            return poss

    def get_fund(self):
        if self.pos_run_mode != "live":
            self.sim_pos.getfundpos()
            self.fund = deepcopy(self.sim_pos.fund)
            for o in self.working_orders.values():
                if o["bs"]=="buy":
                    self.fund["cash"]=self.fund["cash"]-o["price"]*o["qty0"]
            return self.fund
        else:
            """
            cash	float	可用金额
            frozen_cash	float	冻结金额
            market_value	float	持仓市值
            total_asset	float	总资产
            :return:
            """
            if self.account_type=="stock":
                c= self.tradertool.query_stock_asset(self.account)
                fund={"cash":c.cash,"frozen0":c.frozen_cash,"margin":c.market_value,"balance":c.total_asset,"total_balance":c.total_asset}
            else:
                c= self.tradertool.query_credit_detail(self.account)
                print("query_credit_detail",len(c))
                c=c[0]
                fund={"cash":c.m_dAvailable,"frozen0":c.m_dFrozenCash,"margin":c.m_dMarketValue,"balance":c.m_dAssureAsset,"total_balance":c.m_dBalance,"credit":c.m_dTotalDebt}
            for k,v in xt2dict(c).items():
                if k not in fund.keys():
                    fund[k]=v
            self.fund=fund
            print("fund",fund)
            return fund
    def get_trade(self):
        if self.pos_run_mode != "live":
            r=[]
            return r
        else:
            t=self.tradertool.query_stock_trades(self.account)
            trades=[]
            if t:
                for c in t:
                    obj, mkcode = pricetool.code2obj(c.stock_code)
                    oid=c.order_id
                    tt={"obj":obj,"mkcode":mkcode,"timekey":datetime.datetime.fromtimestamp(c.traded_time),"orderid": oid, "sysid": oid}
                    for k, v in xt2dict(c).items():
                        tt[k]=v
                    trades.append(tt)
                    print("trade", tt)
            self.trades=trades
            return trades
    def submit_order(self,order):#报单
        if self.pos_run_mode == "live":
            obj=order["obj"]
            mkcode=order.get("mkcode")
            if mkcode=="":
                mkcode=None
            bs=order["bs"]
            openclose=order["openclose"]
            price=order["price"]
            qty=order["qty"]
            pricetype=order.get("pricetype","limit")
            pricetype=ats2xt_price_type(pricetype)
            ssid=order.get("ssid","")
            msg=order.get("msg","")
            code = pricetool.obj2code(obj, mkcode)
            # order_type = ats2xt_order_type(3, openclose, bs)
            order_type=ats2xt_order_type(self.account.account_type,openclose, bs)
            #order_stock(account, stock_code, order_type, order_volume, price_type, price, strategy_name, order_remark)
            print("submit_order",order)
            print("submit_order-order_stock", "stock_code",code, "order_type",order_type, "order_volume",qty, "price_type",pricetype, "price",price, "strategy_name",ssid, "order_remark",msg,"account",self.investorid)
            if order_type is not None:
                r=self.tradertool.order_stock(self.account, stock_code=code, order_type=order_type, order_volume=qty, price_type=pricetype, price=price, strategy_name=ssid, order_remark=msg)
            else:
                r=-1
            # print("submit_order",order)
            # print("submit_order-order_stock", "stock_code",code, "order_type",order_type, "order_volume",qty, "price_type",pricetype, "price",price, "strategy_name",ssid, "order_remark",msg,"account",self.investorid)
            print("submit_order-r",r)
            # if r==-1:
            #     print("submit_order失败")
            # else:
            #     print("submit_order成功,订单编号",r)
            return r
        else:
            r=super().submit_order_imp(order)
            return r
    def openbuy(self,timekey,qty,posrate=None,amount=None,price=None,msg="",ssid="",obj=None,code=None):
        print("pos openbuy",timekey,qty,posrate,amount,price,msg,ssid,obj,code)
        order={"obj":obj,"bs":"buy","openclose":"open","price":price,"qty":qty,"pricetype":"limit","ssid":ssid,"msg":msg,"code":code}
        r=self.submit_order(order)
        if r==0:
            print("openbuy失败")
            return -1
        else:
            print("openbuy成功,订单编号",r)
            return r
    def creditbuy(self,timekey,qty,posrate=None,amount=None,price=None,msg="",ssid="",obj=None,code=None):
        print("pos creditbuy",timekey,qty,posrate,amount,price,msg,ssid,obj,code)
        order={"obj":obj,"bs":"buy","openclose":"credit","price":price,"qty":qty,"pricetype":"limit","ssid":ssid,"msg":msg,"code":code}
        r=self.submit_order(order)
        if r==0:
            print("creditbuy失败")
            return -1
        else:
            print("creditbuy成功,订单编号",r)
            return r
    def creditsell(self,timekey,qty,posrate=None,amount=None,price=None,msg="",ssid="",obj=None,code=None):
        print("pos creditsell",timekey,qty,posrate,amount,price,msg,ssid,obj,code)
        order={"obj":obj,"bs":"sell","openclose":"credit","price":price,"qty":qty,"pricetype":"limit","ssid":ssid,"msg":msg,"code":code}
        r=self.submit_order(order)
        if r==0:
            print("creditsell失败")
            return -1
        else:
            print("creditsell成功,订单编号",r)
            return r
    def closesell(self,timekey,qty,posrate=None,amount=None,price=None,msg="",ssid="",obj=None,code=None):
        print("pos closesell", timekey, qty, posrate, amount, price, msg, ssid, obj,code)
        order = {"obj": obj, "bs": "sell", "openclose": "close", "price": price, "qty": qty, "pricetype": "limit","code":code,
                 "ssid": ssid, "msg": msg}
        r = self.submit_order(order)
        if r==0:
            print("closesell失败")
            return -1
        else:
            print("closesell成功,订单编号",r)
            return r
    def opensell(self,timekey,qty,posrate=None,amount=None,price=None,msg="",ssid="",obj=None,code=None):
        print("pos opensell",timekey,qty,posrate,amount,price,msg,ssid,obj,code)
        order={"obj":obj,"bs":"sell","openclose":"open","price":price,"qty":qty,"pricetype":"limit","ssid":ssid,"msg":msg,"code":code}
        r=self.submit_order(order)
        if r==0:
            print("opensell失败")
            return -1
        else:
            print("opensell成功,订单编号",r)
            return r
    def closebuy(self,timekey,qty,posrate=None,amount=None,price=None,msg="",ssid="",obj=None,code=None):
        print("pos closebuy", timekey, qty, posrate, amount, price, msg, ssid, obj,code)
        order = {"obj": obj, "bs": "buy", "openclose": "close", "price": price, "qty": qty, "pricetype": "limit","code":code,
                 "ssid": ssid, "msg": msg}
        r = self.submit_order(order)
        if r==0:
            print("closebuy失败")
            return -1
        else:
            print("closebuy成功,订单编号",r)
            return r

    def buy(self,timekey,qty,posrate=None,amount=None,price=None,msg="",ssid="",obj=None,code=None):
        if self.pos_run_mode == "live":
            return self.openbuy(timekey=timekey,qty=qty,posrate=posrate,amount=amount,price=price,msg=msg,ssid=ssid,obj=obj,code=code)
        else:
            oid=uuid.uuid4()
            if code is None:
                code = pricetool.obj2code(obj)
            order={"orderid":oid,"obj":obj,"bs":"buy","openclose":"open","price":price,"qty":qty,"pricetype":"limit","ssid":ssid,"msg":msg,"code":code,
                   "qty0":qty,"qty1":0,"price1":0,"status":"working","statusmsg":"挂单","timekey":timekey}
            order["orderinfo"]=order["msg"]
            self.orders.append(order)
            self.working_orders[oid]=order
            if obj not in self.working_orders_byobj.keys():
                self.working_orders_byobj[obj]={}
            self.working_orders_byobj[obj][oid]=order

    def sell(self,timekey,qty,posrate=None,amount=None,price=None,msg="",ssid="",obj=None,code=None):
        if self.pos_run_mode == "live":
            return self.closesell(timekey=timekey,qty=qty,posrate=posrate,amount=amount,price=price,msg=msg,ssid=ssid,obj=obj,code=code)
        else:
            oid=uuid.uuid4()
            if code is None:
                code = pricetool.obj2code(obj)
            order={"orderid":oid,"obj":obj,"bs":"sell","openclose":"close","price":price,"qty":qty,"pricetype":"limit","ssid":ssid,"msg":msg,"code":code,
                   "qty0":qty,"qty1":0,"price1":price,"status":"working","statusmsg":"挂单","timekey":timekey}
            order["orderinfo"]=order["msg"]

            self.orders.append(order)
            self.working_orders[oid]=order
            if obj not in self.working_orders_byobj.keys():
                self.working_orders_byobj[obj]={}
            self.working_orders_byobj[obj][oid]=order
    def on_order(self,r):
        order = self.deal_order(r)
        print("on_order", order)
        if order["qty0"] > 0:
            self.working_orders[order["orderid"]] = order
        isadd = True
        for i in self.orders:
            if i["orderid"] == order["orderid"]:
                i.update(order)
                isadd = False
                break
        if isadd:
            self.orders.append(order)
    def on_tick_sim(self,t):
        obj=t["obj"]
        self.sim_pos.ontick(t)
        if obj in self.working_orders_byobj.keys():
            oo=self.working_orders_byobj[obj]
            oids=list(oo.keys())
            for oid in oids:
                o=oo[oid]
                lastprice = t["lastPrice"]
                qty1 = 0
                if "hold_qty" not in o.keys():
                    o["hold_qty"]=t["volume"]
                    if o["bs"]=="buy":
                        if lastprice < o["price"]:
                            qty1 = o["qty0"]
                            o["hold_qty"]=0
                    else:
                        if lastprice > o["price"]:
                            qty1 = o["qty0"]
                            o["hold_qty"]=0
                else:
                    o["hold_qty"] = o["hold_qty"]-t["volume"]
                    if o["bs"]=="buy":
                        if lastprice==o["price"]:
                            if o["hold_qty"]<0:
                                qty1=o["qty"]-o["hold_qty"]-o["qty1"]
                        elif lastprice<o["price"]:
                            qty1=o["qty0"]
                            o["hold_qty"] = 0
                    else:
                        if lastprice==o["price"]:
                            if o["hold_qty"]<0:
                                qty1=o["qty"]-o["hold_qty"]-o["qty1"]
                        elif lastprice>o["price"]:
                            qty1=o["qty0"]
                            o["hold_qty"] = 0
                if qty1>0:
                    if qty1>=o["qty0"]:
                        qty1=o["qty0"]
                    #成交qty  价格委托价,计入模拟持仓
                    if o["bs"]=="buy":
                        r=self.sim_pos.buy(timekey=t["timekey"], obj=obj, price=o["price"],qty=qty1,msg=o["msg"],is_commission=False)
                    else:
                        r,qty1=self.sim_pos.sell(timekey=t["timekey"], obj=obj, price=o["price"],qty=qty1,msg=o["msg"],is_commission=False)
                    if r:
                        qty0=o["qty0"]-qty1
                        o["qty0"]=qty0
                        o["qty1"]=o["qty1"]+qty1
                        o["statusmsg"]="部分成交"
                        o["status"] = "parttraded"
                        o["tradetimekey"]=t["timekey"]
                        if qty0<0.001:
                            o["statusmsg"] = "成交"
                            o["status"] = "alltraded"
                            del oo[oid]
                            if oid in self.working_orders.keys():
                                del self.working_orders[oid]
                            if len(oo)==0:
                                del self.working_orders_byobj[obj]
                            print("订单完成", o)
                    else:
                        o["qty0"]=0
                        o["statusmsg"]="撤单"
                        o["status"] = "canceled"
                        o["tradetimekey"]=t["timekey"]
                        del oo[oid]
                        if oid in self.working_orders.keys():
                            del self.working_orders[oid]
                        if len(oo) == 0:
                            del self.working_orders_byobj[obj]




if  __name__=="__main__":
    import sys
    from jili.core.config import load_config_json

    c = load_config_json(r"E:\qjs\zjhhzc\stoppl\ss\multifactor\pos_calc_live\config.json")
    print("config.json:", c)
    t0=tradetool(c)